package arthm_manip

import (
	"fmt"
	"go/ast"
	"go/token"
	"manip_exec"
)

type ArthmManipulator struct {
}

var astNodes = make([]ast.Node, 0)

func (m ArthmManipulator) ManipulateAST(f ast.Node, index int) (manip_exec.ManiRes, error) {
	
	astNodes = make([]ast.Node, 0)		//This has to be reset as well

	// Walk through the AST & Build astNodes
	v := astVisitor{}
	ast.Walk(v, f)

	binExprNodes := make([]*ast.BinaryExpr, 0)		//Reset this slice

	for _, node := range astNodes {
		switch x := node.(type) {
		case *ast.BinaryExpr:
			switch x.Op {
			case token.ADD, token.SUB, token.MUL, token.QUO, token.REM:
					binExprNodes = append(binExprNodes, x)
			}
		}
	}
	var random int
	if len(binExprNodes) > 0 {
	random = index % len(binExprNodes)
	} else {		//Force build failure
		astNodes[0] = nil
		retString := "Nothing manipulated"
		result := manip_exec.ManiRes{[]int{}, retString}
		return result, nil		
	}
	x := (binExprNodes[random])
	oldOp := (*x).Op
	// for _, v := range binExprNodes {			//Debugging
	// 	fmt.Println(v)
	// }

	if (*x).Op == token.ADD {
		(*x).Op = token.SUB
	} else if (*x).Op == token.SUB {
		(*x).Op = token.ADD
	} else if (*x).Op == token.MUL {
		(*x).Op = token.QUO
	} else if (*x).Op == token.QUO {
		(*x).Op = token.MUL
	} else if (*x).Op == token.REM {
		(*x).Op = token.MUL
	}

	retString := fmt.Sprintf("Changed %s to %s in Line: ", oldOp.String(), (*x).Op.String())
	result := manip_exec.ManiRes{[]int{int((*x).Pos())}, retString}
	return result, nil
}

func (m ArthmManipulator) String() string {
	return "arthm"
}

//Walker Vistor struct
type astVisitor struct {
}

//Walker Visitor function
func (v astVisitor) Visit(node ast.Node) (w ast.Visitor) {
	if node != nil {

		astNodes = append(astNodes, node) //Builds the Node Slice
	}
	return ast.Visitor(v)
}

